/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.datalayer.daoimpl;

import cz.fi.muni.diplomka.common.Coordinates;
import cz.fi.muni.diplomka.common.enumeration.BuildingType;
import cz.muni.fi.diplomka.datalayer.dao.BuildingInProgressDao;
import cz.muni.fi.diplomka.datalayer.entity.BuildingInProgressEntity;
import cz.muni.fi.diplomka.datalayer.util.DbConnector;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;

/**
 *
 * @author Jakub Kutil
 */
public class BuildingInProgressDaoImpl implements BuildingInProgressDao {

    @Override
    public void addBuildingInProgress(BuildingInProgressEntity buildingInProgressEntity) {
        if (buildingInProgressEntity == null) {
            throw new IllegalArgumentException("Building In Progress Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", buildingInProgressEntity.getCityId());
            params.put("xCoordinate", buildingInProgressEntity.getCoordinates().getX_coordinate());
            params.put("yCoordinate", buildingInProgressEntity.getCoordinates().getY_coordinate());
            params.put("timeOfFinish", buildingInProgressEntity.getTimeOfFinish().getTime());
            params.put("name", buildingInProgressEntity.getName().toString());
            String query = "MATCH (City:City {id : {cityId}}) "
                    + "CREATE (BuildingInProgress: BuildingInProgress {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}, "
                    + "timeOfFinish : {timeOfFinish}, name : {name}})<-[r:BEINGBUILT]-(City)";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void editBuildingInProgress(BuildingInProgressEntity buildingInProgressEntity) {
        if (buildingInProgressEntity == null) {
            throw new IllegalArgumentException("Building In Progress Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", buildingInProgressEntity.getCityId());
            params.put("xCoordinate", buildingInProgressEntity.getCoordinates().getX_coordinate());
            params.put("yCoordinate", buildingInProgressEntity.getCoordinates().getY_coordinate());
            params.put("timeOfFinish", buildingInProgressEntity.getTimeOfFinish().getTime());
            params.put("name", buildingInProgressEntity.getName());

            String query = "MATCH (City{id : {cityId} })-[r:BEINGBUILT]-> (BuildingInProgress:BuildingInProgress { xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}})"
                    + " SET BuildingInProgress.timeOfFinish = {timeOfFinish}, BuildingInProgress.name = {name}";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void deleteBuildingInProgress(Long cityId, Coordinates coordinates) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        if (coordinates == null) {
            throw new IllegalArgumentException("Coordinates can not be null.");
        }
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);
            params.put("xCoordinate", coordinates.getX_coordinate());
            params.put("yCoordinate", coordinates.getY_coordinate());

            String query = "MATCH (City:City{ id : {cityId}})-[r:BEINGBUILT]->"
                    + "(BuildingInProgress:BuildingInProgress {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}}) "
                    + "DELETE BuildingInProgress,r";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public BuildingInProgressEntity getBuildingInProgressAtPosition(Long cityId, Coordinates coordinates) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        if (coordinates == null) {
            throw new IllegalArgumentException("Coordinates can not be null.");
        }
        BuildingInProgressEntity buildingInProgressEntity = new BuildingInProgressEntity();

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);
            params.put("xCoordinate", coordinates.getX_coordinate());
            params.put("yCoordinate", coordinates.getY_coordinate());

            String query = "MATCH (City:City{ id : {cityId}})-[r:BEINGBUILT]->(BuildingInProgress:BuildingInProgress {xCoordinate : {xCoordinate}, yCoordinate : {yCoordinate}}) "
                    + "RETURN BuildingInProgress";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> buildings = result.columnAs("BuildingInProgress");
            while (buildings.hasNext()) {
                Node node = buildings.next();

                buildingInProgressEntity.setCityId(cityId);
                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                buildingInProgressEntity.setCoordinates(c);
                buildingInProgressEntity.setTimeOfFinish(new Timestamp(Long.valueOf(node.getProperty("timeOfFinish").toString())));
                buildingInProgressEntity.setName(BuildingType.valueOf(node.getProperty("name").toString()));

            }
            t.success();
        }
        return buildingInProgressEntity;
    }

    @Override
    public List<BuildingInProgressEntity> getAllBuildingsInProgressForCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("City Id can not be null.");
        }
        List<BuildingInProgressEntity> buildingList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("cityId", cityId);

            String query = "MATCH (City:City{ id : {cityId}})-[r:BEINGBUILT]->(BuildingInProgress:BuildingInProgress) RETURN BuildingInProgress";
            ExecutionResult result = engine.execute(query, params);
            Iterator<Node> buildings = result.columnAs("BuildingInProgress");
            while (buildings.hasNext()) {
                Node node = buildings.next();
                BuildingInProgressEntity buildingInProgressEntity = new BuildingInProgressEntity();
                buildingInProgressEntity.setCityId(cityId);
                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                buildingInProgressEntity.setCoordinates(c);
                buildingInProgressEntity.setTimeOfFinish(new Timestamp(Long.valueOf(node.getProperty("timeOfFinish").toString())));
                buildingInProgressEntity.setName(BuildingType.valueOf(node.getProperty("name").toString()));

                buildingList.add(buildingInProgressEntity);
            }
            t.success();
        }
        return buildingList;
    }

    @Override
    public List<BuildingInProgressEntity> getAllBuildingsInProgress() {
        List<BuildingInProgressEntity> buildingList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            String query = "MATCH (City:City)-[r:BEINGBUILT]->(BuildingInProgress:BuildingInProgress) RETURN BuildingInProgress,City.id";
            ExecutionResult result = engine.execute(query, params);
            ResourceIterator<Map<String, Object>> buildings = result.iterator();

            while (buildings.hasNext()) {
                Map<String, Object> row = buildings.next();
                Node node = (Node) row.get("BuildingInProgress");

                BuildingInProgressEntity buildingInProgressEntity = new BuildingInProgressEntity();
                buildingInProgressEntity.setCityId(new Long(row.get("City.id").toString()));
                Coordinates c = new Coordinates();
                c.setX_coordinate(Integer.valueOf(node.getProperty("xCoordinate").toString()));
                c.setY_coordinate(Integer.valueOf(node.getProperty("yCoordinate").toString()));
                buildingInProgressEntity.setCoordinates(c);
                buildingInProgressEntity.setTimeOfFinish(new Timestamp(Long.valueOf(node.getProperty("timeOfFinish").toString())));
                buildingInProgressEntity.setName(BuildingType.valueOf(node.getProperty("name").toString()));

                buildingList.add(buildingInProgressEntity);
            }
            t.success();
        }
        return buildingList;
    }
}
